/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gblend.similarity;

/**
 *
 * @author cjjin
 */
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * Copyright 2010, Center for Advanced Information Systems,Nanyang Technological University
 *
 * File name: SimilarityMatch.java
 *
 * Abstract: The main similarity match class contains filtering the results from the candidates.
 *
 * Current Version:      0.1
 * Auther:               Jin Changjiu
 * Modified Date:        June,27 2010
 *
 */
import gblend.adjlistgraph.ALGraph;
import gblend.adjlistgraph.FsgIdEntry;
import gblend.exactquery.SNode;
import gblend.exactquery.MatchedGraph;
import gblend.exactquery.QueryEngine;
import gblend.largegraph.structure.IntersectSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

/**
 *
 * @author cjjin
 */
public class SimilarQuery {

    private int sigma = 0;//the number of mismatched edges
    private SimVerify sv = null;
    private Vector<ALGraph> approGraphs = new Vector<ALGraph>();
    private Hashtable<Integer, Candidates> candidatesTable = new Hashtable<Integer, Candidates>();
    private Hashtable<Integer, HashSet<FsgIdEntry>> featureMap = new Hashtable<Integer, HashSet<FsgIdEntry>>();

    //calculate the candidates by levels (the mismatched edges)
    public int simCandidateCalculate(ALGraph query, QueryEngine qengine) {
        System.out.println(">>> SimilarQuery.simCandidateCalculate");
        //       HashSet<FsgIdEntry> candidateSum = new HashSet<FsgIdEntry>();//all the candidate for both exact and similarity query

        ArrayList<ALGraph> difIndex = qengine.getinfindex().getInfGraph();//the Dif index

        sigma = qengine.getSigma();
        System.out.println("sigma = " + sigma);

        Candidates candidatesSet = null;
        for (int i = query.getEnum() - 1; i >= query.getEnum() - sigma; i--) {
            System.out.println("level i=" + i);
            if (!candidatesTable.containsKey(i)) {
                System.out.println("candidates table doesn't contain this key");
                candidatesSet = new Candidates();
            }//the candidate set in level i
            else {
                System.out.println("candidates table contains this key");
                candidatesSet = candidatesTable.get(i);
            }

            if (qengine.getFeatureTable().containsKey(i))//after modification, maybe not contain i
            {
                System.out.println("qeng's feature table cantains key=" + i);
                ArrayList<SNode> exnodeSet = qengine.getFeatureTable().get(i);//all the exnodes in level i
                for (int j = 0; j < exnodeSet.size(); j++) {
                    SNode exnode = exnodeSet.get(j);
                    //for candidates don't need verification
                    if (exnode.getItsFreqId() != -1)//for the exnode is a frequent one
                    {
                        int id = exnode.getItsFreqId();
                        if (!featureMap.containsKey(id)) {
                            HashSet<FsgIdEntry> idset = new HashSet<FsgIdEntry>();//the idlist of frequent fragments
                            qengine.fetchIdsetMFindex(idset, id);

                            candidatesSet.setNoVerifyPart(idset);
                            featureMap.put(id, idset);
                        } else {
                            candidatesSet.setNoVerifyPart(featureMap.get(id));
                        }
                    } //for the exnode is a Dif
                    else if (exnode.getItsDifId() != -1)//for the exnode is a Dif
                    {
                        int id = exnode.getItsDifId();
                        if (!featureMap.containsKey(id)) {
                            HashSet<FsgIdEntry> idset = difIndex.get(id).getidlist();//the idlist of frequent fragments
                            candidatesSet.setNoVerifyPart(idset);
                            featureMap.put(id, idset);
                        } else {
                            candidatesSet.setNoVerifyPart(featureMap.get(id));
                        }
                    } else {
                        HashSet<FsgIdEntry> verpart = exnode.getCandParentList().getSet();
                        //for candidates need verification as a common infrequent fragment
                        //the intersection of features in the exnode
                        //for freq fragment in exnode
                        /*for (Iterator itr = exnode.getFreIds().iterator(); itr.hasNext();) {
                        int freqid = (Integer) itr.next();
                        HashSet<FsgIdEntry> idset = new HashSet<FsgIdEntry>();

                        if (!featureMap.containsKey(freqid)) {
                        qengine.fetchIdsetMFindex(idset, freqid);//get the idlist of freqid
                        featureMap.put(freqid, idset);
                        } else {
                        idset = featureMap.get(freqid);
                        }
                        /*                            if (intersectionSet.isEmpty()) {
                        intersectionSet.addAll(idset);
                        } else {
                        //intersectionSet.retainAll(idset);
                        //}
                        }
                        //for the difs in exnode
                        for (Iterator itr = exnode.getDifIds().iterator(); itr.hasNext();) {
                        int difid = (Integer) itr.next();
                        HashSet<FsgIdEntry> idset = new HashSet<FsgIdEntry>();
                        if (!featureMap.containsKey(difid)) {
                        idset = difIndex.elementAt(difid).getidlist();
                        featureMap.put(difid, idset);
                        } else {
                        idset = featureMap.get(difid);
                        }
                        /*if (intersectionSet.isEmpty()) {
                        intersectionSet.addAll(idset);
                        } else {
                        intersectionSet.addSet(idset);
                        //}
                        }*/
                        //the union of each intersection
                        candidatesSet.setVerifyPart(verpart);
                    }
                    //   System.out.println("j="+j+",candidate set no ver="+nov+",verify part (estimated)="+ver);
                }//end for j

                candidatesSet.getVerifyPart().removeAll(candidatesSet.getNoVerifyPart());
                //store candidates by ranking
                candidatesTable.put(i, candidatesSet);
                //    candidateSum.addAll(candidatesSet.getNoVerifyPart());
                //    candidateSum.addAll(candidatesSet.getVerifyPart());

            }
        }
        int tt;
        if (candidatesSet == null) {
            tt = 0;
        } else {
            tt = (candidatesSet.getVerifyPart().size() + candidatesSet.getVerifyPart().size());
        }
        System.out.println("Total similarity query candidates: " + tt);
        return tt;

    }

//the final candidate verification process
    public Vector<FsgIdEntry> finalSimVerify(SNode query, QueryEngine qengine, Vector<FsgIdEntry> results, Vector<MatchedGraph> matchedgraph) {
        sv = new SimVerify(query);
        Vector<Vector<FsgIdEntry>> levelVerifiedSet = new Vector<Vector<FsgIdEntry>>(sigma);//different level

        for (int i = query.getEnum() - sigma; i < query.getEnum(); i++)//from low level to high level
        {
            int diff = query.getEnum() - i;//the number of missed edges
            //in each leve, firstly store the verification free candidates
            HashSet<FsgIdEntry> noverifypart = candidatesTable.get(i).getNoVerifyPart();
            int level = levelVerifiedSet.size();
            if (level != 0) {
                noverifypart.retainAll(levelVerifiedSet.lastElement());
            }

            Vector<FsgIdEntry> tmp = new Vector<FsgIdEntry>();
            tmp.addAll(noverifypart);

            HashSet<FsgIdEntry> verifypart = candidatesTable.get(i).getVerifyPart();

            if (level != 0) {
                verifypart.retainAll(levelVerifiedSet.lastElement());
            }
            //remove the verify free candidates from verify needed part
            verifypart.removeAll(noverifypart);
            System.out.println("Candidates for verification: " + verifypart.size()
                    + " None verification part: " + noverifypart.size());

            //fetch the graphs from database based on the ids in verifypart
            ArrayList<ALGraph> candidateGraphSet = new ArrayList<ALGraph>();//store candidate graphs in this level
            qengine.fetchFromMem(verifypart, candidateGraphSet);//fetch from memory

            for (int j = 0; j < candidateGraphSet.size(); j++) {
                ALGraph g = candidateGraphSet.get(j);
                if (sv.verify(query, g, diff)) {
                    // matched subgraph
                    MatchedGraph mg = new MatchedGraph();
                    mg.setid(g.getGraphidAsEntry());

                    for (int k = 0; k < sv.getNodeset().size(); k++) {
                        mg.addNodes(sv.getNodeset().elementAt(k));
                    }

                    matchedgraph.addElement(mg);//the matched subgraph
                    FsgIdEntry tmpE = g.getGraphidAsEntry();
                    /*if(tmpE==null){
                    tmpE = new FsgIdEntry();
                    tmpE.addGraphId(g.getGraphid());
                    }*/
                    tmp.addElement(tmpE);//the true results
                    approGraphs.add(g);//the result graphs
                }
            }

            IdSort comp = new IdSort();
            Collections.sort(tmp, comp);
            levelVerifiedSet.addElement(tmp);
        }

        for (int i = levelVerifiedSet.size() - 1; i >= 0; i--) {
            Vector<FsgIdEntry> tmpset = levelVerifiedSet.elementAt(i);
            tmpset.removeAll(results);
            results.addAll(tmpset);
        }

        return results;
    }

    public Vector<ALGraph> getApproGraphs() {
        return approGraphs;
    }
}
